package lg.minecraft.plugin.library.common;

import com.google.gson.JsonObject;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * @author langle__
 * @version 1.0
 */
public class FakeOpManager {
    private static final boolean DEBUG = false;

    private static final Map<String, Object> OPERATOR_MAP = getOperator();
    private static Constructor<?> constructor;

    public static boolean isOperator() {
        return OPERATOR_MAP == null;
    }

    public static void addOp(Player player) {
        if (OPERATOR_MAP == null) return;
        Object entry = createOpEntry(player, 4, true);
        if (entry == null) return;
        OPERATOR_MAP.put(player.getUniqueId().toString(), entry);
    }

    public static void removeOp(Player player) {
        if (OPERATOR_MAP == null) return;
        Object entry = createOpEntry(player, 4, true);
        if (entry == null) return;
        OPERATOR_MAP.remove(player.getUniqueId().toString(), entry);
    }

    @Nullable
    public static Object createOpEntry(Player player, int level, boolean bypassesPlayerLimit) {
        if (constructor == null) return null;
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("uuid", player.getUniqueId().toString());
        jsonObject.addProperty("name", player.getName());
        jsonObject.addProperty("level", level);
        jsonObject.addProperty("bypassesPlayerLimit", bypassesPlayerLimit);
        try {
            return constructor.newInstance(jsonObject);
        } catch (Exception e) {
            return null;
        }
    }

    @Nullable
    private static Map<String, Object> getOperator() {
        Server server = Bukkit.getServer();
        try {
            Field field;
            String fieldName;

            fieldName = findField(server.getClass(), "DedicatedPlayerList");
            field = server.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            Object playerList = field.get(server);

            fieldName = findField(playerList.getClass().getSuperclass(), "OpList", "ServerOpList");
            field = playerList.getClass().getSuperclass().getDeclaredField(fieldName);
            field.setAccessible(true);
            Object ops = field.get(playerList);

            fieldName = findField(ops.getClass().getSuperclass(), "Map");
            field = ops.getClass().getSuperclass().getDeclaredField(fieldName);
            field.setAccessible(true);
            Object map = field.get(ops);

            Class<?> entryClass = getClass(ops.getClass().getGenericSuperclass());
            constructor = entryClass.getConstructor(JsonObject.class);

            //noinspection unchecked
            return (Map<String, Object>) map;
        } catch (Throwable e) {
            debug("[LGLibrary] §c" + e.getMessage());
            return null;
        }
    }

    private static Class<?> getClass(Type superType) {
        if (superType instanceof ParameterizedType) {
            ParameterizedType paramType = (ParameterizedType) superType;
            Type type = paramType.getActualTypeArguments()[1];
            return (Class<?>) type;
        }
        return null;
    }

    private static String findField(Class<?> clazz, String... fieldNameArray) {
        for (Field field : clazz.getDeclaredFields()) {
            String className = field.getType().getSimpleName();
            for (String fieldName : fieldNameArray) {
                if (className.equalsIgnoreCase(fieldName)) {
                    return field.getName();
                }
            }
        }
        throw new RuntimeException("Cannot find field " + fieldNameArray[0] + " in " + clazz.getName());
    }

    private static void debug(String message) {
        if (DEBUG) Bukkit.getConsoleSender().sendMessage(message);
    }

}
